# Copyright 2007 pyPayPal Dev Team
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 only
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

import constants, objects
from urllib import urlencode
from paypal_exceptions import *
class _Payment():
    def __init__(self):
        self._method = None
        self._action = None
        self.total = 0
        
        self.response_transaction_id = None
        self.response_amount = None
    
    def is_valid(self):
        """ Checks to make sure that total is within Bounds """
        if ( self.total < constants.MIN_VALUE or self.total >  constants.MAX_VALUE ):
            raise ValueOutOfBoundsError, 'total must be Greater than 0 and less than 10,000 USD'
        else:
            pass

class _DirectPayment(_Payment):
    """ Base Payment class. """
    def __init__(self, credit_card = None, billing_address = None, ip = None , shipping_address=None, subtotal = None, cart=None):
        _Payment.__init__(self)
        self.billing_address = billing_address
        self.credit_card = credit_card
        
        self.shipping_address = shipping_address
        self.cart = cart
        self.subtotal = subtotal
        self.ip = ip
        
        self._method = 'DoDirectPayment'
        
    def is_valid(self):
        """ Checks the class to make sure all necessary objects and values are present """
        
        if not ( self.billing_address and self.credit_card and self.ip ):
            raise IncompleteObjectError, 'DoDirectPayment object is incomplete'

        # Check the address and credit card objects
        self.billing_address.is_valid()
        self.credit_card.is_valid()
        
        # Check the Extras
        if self.shipping_address:
            self.shipping_address.is_valid()
        if self.cart: 
            if not self.subtotal:
                raise IncompleteObjectError, 'You must specify a subtotal if you specify a cart'
            self.cart.is_valid()
        if self.subtotal:
            self.subtotal.is_valid()
        
        #Check the amount    
        _Payment.is_valid(self)
        
    def get_uri(self):
        """ Return full transaction URI for proccessing. """
        actions = {'METHOD': self._method,
        'PAYMENTACTION': self._action,
        'IPADDRESS' : self.ip,
        }
        uri = [urlencode(actions), self.billing_address.get_uri(), urlencode({'AMT': self.total}), self.credit_card.get_uri()]
        if self.shipping_address: 
            uri.append(self.shipping_address.get_uri())
        #if self.cart:   # Commented out only temporarily until API is fully working
        #    uri.append(self.cart.get_uri())
        return '&'.join(uri)              
    
    def get_response_values(self, response):
        """ A function that gets the needed respone values from the response object, and stores them within the class """
        self.response_transaction_id = response.tokens['TRANSACTIONID']
        self.response_amount = response.tokens['AMT']

class _Express_Checkout(_Payment):         
    """ Handles the express checkout feature of paypal 
        Usefule for when the customer already has a paypal account
        This class has to be passed through the interface 3 seperate times.  This is because it needs to 
        communicate with pay pal 3 different times before this type of transaction is completed.  Dont worry though,
        all of it is handled automatically.  All you have to do is set the appropriate classes, and the class will handle the rest
        
        Note:  This class has 3 special features 
        1. You can request that no shipping information be returend from paypal by setting the no_shipping_flag to 1
        2. You can retquire the shipping address to be confirmed by setting the require_confirmed_address_flag to 1
        3. You can override the user's shipping address in paypal by passing in a Shipping_Address object to shipping_address
            Note:  If you leave this blank, paypal will assume you want to use the user's shipping address.
                    You can find it in this class after the second passing call to paypal (it will be of type object.Shipping_Address)"""
    def __init__(self, return_url = None, cancel_url = None, require_confirmed_address_flag = 0, no_shipping_flag = 0):
        _Payment.__init__(self)
        
        self._token = None    #READ ONLY
        self._payer_id = None #READ ONLY
        self.no_shipping_flag = no_shipping_flag
        self.require_confirmed_address_flag = require_confirmed_address_flag
        
        self.shipping_address = None #objects.Shipping_Address()
        
        self.redirect_url = None
        self.return_url = return_url
        self.cancel_url = cancel_url
        
        self._action = None
        #Method varies with state
        
    def is_valid(self):
        """ Required Validation Class """
        if not ( self.return_url and self.cancel_url ):
            raise IncompleteObjectError, 'You must specify a return_url and/or a cancel_url'
        
        _Payment.is_valid(self)
        
    def get_uri(self):
        """ Handles the calls from the interface """
        address_override = 0
        
        if not self._token:
            # Then we do not have a token Yet.  Get one!
            actions = {'METHOD'        : 'SetExpressCheckout',
                       'PAYMENTACTION' : self._action,
                       'AMT'           : self.total,
                       'RETURNURL'     : self.return_url,
                       'CANCELURL'     : self.cancel_url
                       }
            if self.shipping_address:
                actions['ADDROVERRIDE'] = 1
                address_override = 1
                
        elif not self._payer_id:
            # Then we do not have details yet.  We need to get them
            actions = {'METHOD'        : 'GetExpressCheckoutDetails',
                       'TOKEN'         : self._token
                       }
        else:
            # Then we must complete the sale according to the class
            actions = {'METHOD'        : 'DoExpressCheckoutPayment',
                       'TOKEN'         : self._token,
                       'PAYMENTACTION' : self._action,
                       'AMT'           : self.total,
                       'PAYERID'       : self._payer_id
                       }
        
        if address_override:
            # user wants to override paypal shipping address
            uri=[urlencode(actions), self.shipping_address.get_uri()]
            return '&'.join(uri)
        else:       
            return urlencode(actions)
    
    def get_response_values(self, response):
        """ Called when the Response Parser has completed successfully """
        if not self._token:
            #then get the token from the response
            self._token = response.tokens['TOKEN']
            self.redirect_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=%s" %self._token
            
        elif not self._payer_id:
            #then we need to get the ship information
            self._payer_id = response.tokens['PAYERID']  
            
            if not (self.no_shipping_flag or self.shipping_address):
                # Then parse out the shipping address
                self.shipping_address = objects.Shipping_Address()
                fullname = response.tokens['SHIPTONAME']
                #elf.shipping_address.first_name = 
                #elf.shipping_address.last_name =
                self.shipping_address.address_line_one = response.tokens['SHIPTOSTREET']
                #self.shipping_address.address_line_two = response.tokens['SHIPTOSTREET2']
                self.shipping_address.city = response.tokens['SHIPTOCITY']
                self.shipping_address.state = response.tokens['SHIPTOSTATE']
                self.shipping_address.zipcode = response.tokens['SHIPTOZIP']
                self.shipping_address.country_code = response.tokens['SHIPTOCOUNTRYCODE']
                
                # More to come ?
        else:
            # We need to get the Transaction ID
            self.response_transaction_id = response.tokens['TRANSACTIONID']
            self.response_amount = response.tokens['AMT']


class _Capture():
    """
    This is the parent class for all capture functions.  Captures are used to get payment on previously done authorizations.
    There are two types of captures, Partial and Complete.
    """
    def __init__(self, transaction_id = None, outstanding_amount = None, capture_amount = None, invoice_number = None, note = None):
        self._method = 'DoCapture'
        self._type = None
        self.transaction_id = transaction_id
        self.outstanding_amount = outstanding_amount    #Amount left to capture
        self.invoice_number = invoice_number
        self.note = note
        
    def is_valid(self):
        pass
        
    def get_uri(self):
        #define self.type Here.
        pass
    
    def get_response_values(self, response):
        pass